package org.jetlinks.pro.datasource;

import lombok.Generated;
import lombok.Getter;
import org.hswebframework.web.bean.FastBeanCopier;

import javax.annotation.Nonnull;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Getter
public abstract class AbstractDataSource<C> implements DataSource {

    private final String id;

    private C config;

    @SuppressWarnings("all")
    private final Map<Class<?>, CommandHandler> handlers = new ConcurrentHashMap<>();

    private volatile boolean disposed;

    public AbstractDataSource(String id,
                              C config) {
        this.id = id;
        this.config = config;
    }

    @Override
    @Generated
    public final String getId() {
        return id;
    }

    @Override
    public abstract DataSourceType getType();

    protected final <CMD extends Command<R>, R> void registerHandler(Class<CMD> type, CommandHandler<CMD, R> handler) {
        handlers.put(type, handler);
    }

    @Nonnull
    @Override
    public final <R> R execute(@Nonnull Command<R> command) {
        if (isDisposed()) {
            throw new IllegalStateException("datasource " + id + " is disposed");
        }
        @SuppressWarnings("all")
        CommandHandler<Command<R>, R> handler = handlers.get(command.getClass());
        if (handler == null) {
            return handleNoHandlerCommand(command);
        }
        return handler.handle(this, command);
    }

    @Override
    public final void dispose() {
        disposed = true;
        doOnDispose();
    }

    @Override
    public final boolean isDisposed() {
        return disposed;
    }

    @Generated
    public final C getConfig() {
        return config;
    }

    @SuppressWarnings("all")
    public C copyConfig() {
        return (C) FastBeanCopier.copy(config, config.getClass());
    }

    public final void setConfig(C config) {
        C old = this.config;
        this.config = config;
        handleSetConfig(old, config);
    }

    protected void handleSetConfig(C oldConfig, C newConfig) {


    }

    protected void doOnDispose() {

    }

    protected <R> R handleNoHandlerCommand(Command<R> command) {
        throw new UnsupportedOperationException("no handler for command " + command);
    }
}
